import deepseek_local as dsl
import whisper , opencc
import json
from langchainLLM import langchainLLM
from image_process import ImageWidget
from paddleocr import PaddleOCR
from microphone_listener import MicrophoneListener
from PyQt5.QtCore import QThread, pyqtSignal
from langchain_core.runnables.utils import AddableDict

#定义一个线程类
class getResponse_Thread(QThread):
    #自定义信号声明
    # 使用自定义信号和UI主线程通讯，参数是发送信号时附带参数的数据类型，可以是str、int、list等
    finishSignal = pyqtSignal(str)          # 回答结束的信号
    StreamResponseSignal = pyqtSignal(str)  # 流式的时候返回每个帧的触发信号

    def __init__(self,parent=None):
        super(getResponse_Thread, self).__init__(parent)
        self.conversation_list = []         # 对话记录表，用于记录上下文
        self.prompt = ''
        self.answer = ''
        self.response = None
        
    # run函数是子线程中的操作，线程启动后开始执行
    def run(self):
        self.response = dsl.call_deepseek(
            prompt = self.prompt,
            conversationList = self.conversation_list,
            Contextual_relevance_on = True
            )  # 流式输出llm返回的信息
        self.conversation_list.append({'role':'user','content':self.prompt})

        if self.response.status_code == 200:
            try:
                for index in self.response.iter_content(chunk_size=8192,decode_unicode=True):
                    # 发射自定义信号
                    # 通过emit函数将参数chunk传递给主线程，触发自定义信号
                    chunk = json.loads(index)
                    # print(chunk)
                    # print(self.conversation_list)
                    self.answer = self.answer + chunk["response"]
                    self.StreamResponseSignal.emit(chunk["response"])  # 注意这里与_signal = pyqtSignal(str)中的类型相同
                    
                    # 外部中断子线程运行
                    if self.isInterruptionRequested():      # 子线程处理中断申请
                        self.response.close()               # 关闭与deepseek接口之间的通信
                        self.StreamResponseSignal.emit('\n\n\n*<生成被中断了>*')
                        self.finishSignal.emit(self.answer + '\n\n\n*<生成被中断了>*') # 返回最后的生成文本
                        self.exit()                         # 退出子线程
            except Exception as e:
                print(f"大模型回答出现异常：{e}")
                self.conversation_list.append({'role':'assistant','content':self.answer})
                self.finishSignal.emit(self.answer) # 回答结束后返回回答的完整文本内容
            else:
                self.conversation_list.append({'role':'assistant','content':self.answer})
                self.finishSignal.emit(self.answer) # 回答结束后返回回答的完整文本内容
        else:
            print("请求失败，状态码：" + str(self.response.status_code))

        self.answer = ''

#定义一个线程类
class getLangChainResponse_Thread(QThread):
    #自定义信号声明
    # 使用自定义信号和UI主线程通讯，参数是发送信号时附带参数的数据类型，可以是str、int、list等
    finishSignal = pyqtSignal(str)          # 回答结束的信号
    StreamResponseSignal = pyqtSignal(str)  # 流式的时候返回每个帧的触发信号
    onlineSearchingSignal = pyqtSignal(str)

    def __init__(self,parent=None):
        super(getLangChainResponse_Thread, self).__init__(parent)
        self.llm = langchainLLM()
        self.prompt = ''
        self.answer = ''
        self.is_online = False
        self.response = None
        self.doc_list = []
        self.conversation_list = []
        
    # run函数是子线程中的操作，线程启动后开始执行
    def run(self):
        if(self.doc_list == []):
            if(self.is_online):
                self.onlineSearchingSignal.emit('start')
                self.llm.loadDocument(search_online = True,query = self.prompt)
                self.response = self.llm.retrieval_chain.stream({"input":self.prompt})
            else:
                self.response = self.llm.callLLM(prompt = self.prompt,isStream = True)
            
        else:
            # self.response = self.llm.callLLM(prompt = self.prompt,isStream = True,combineDocuments = True)
            self.response = self.llm.retrieval_chain.stream({"input":self.prompt})
            print(self.response)

        self.conversation_list.append({'role':'user','content':self.prompt})

        
        try:
            if(self.doc_list == []):
                for chunk in self.response:
                    # 发射自定义信号
                    # 通过emit函数将参数chunk传递给主线程，触发自定义信号
                    # print(chunk)
                    # print(self.conversation_list)
                    if(self.is_online):
                        self.answer = f"{self.answer}{chunk.get('answer')}"
                        self.StreamResponseSignal.emit(chunk.get('answer'))  # 注意这里与_signal = pyqtSignal(str)中的类型相同
                    else:
                        self.answer = self.answer + chunk
                        self.StreamResponseSignal.emit(chunk)  # 注意这里与_signal = pyqtSignal(str)中的类型相同
                    
                    # 外部中断子线程运行
                    if self.isInterruptionRequested():      # 子线程处理中断申请
                        self.StreamResponseSignal.emit('\n\n\n*<生成被中断了>*')
                        self.finishSignal.emit(self.answer + '\n\n\n*<生成被中断了>*') # 返回最后的生成文本
                        # self.exit()                         # 退出子线程
                        # del self.response
                        break
            else:
                for chunk in self.response:
                    # 发射自定义信号
                    # 通过emit函数将参数chunk传递给主线程，触发自定义信号
                    # print(chunk)
                    self.answer = f"{self.answer}{chunk.get('answer')}"
                    self.StreamResponseSignal.emit(chunk.get('answer'))  # 注意这里与_signal = pyqtSignal(str)中的类型相同
                    
                    # 外部中断子线程运行
                    if self.isInterruptionRequested():      # 子线程处理中断申请
                        self.StreamResponseSignal.emit('\n\n\n*<生成被中断了>*')
                        self.finishSignal.emit(self.answer + '\n\n\n*<生成被中断了>*') # 返回最后的生成文本
                        # self.exit()                         # 退出子线程
                        # del self.response
                        break
            
        except Exception as e:
            print(f"大模型回答出现异常：{e}")
            self.conversation_list.append({'role':'assistant','content':self.answer})
            self.finishSignal.emit(self.answer) # 回答结束后返回回答的完整文本内容
        else:
            self.conversation_list.append({'role':'assistant','content':self.answer})
            self.finishSignal.emit(self.answer) # 回答结束后返回回答的完整文本内容
        finally:
            self.answer = ''

class UpLoadImage_Thread(QThread):                  # 上传图片线程
    imageWidgetCmdSignal = pyqtSignal(list)
    finishSignal = pyqtSignal(str)                  # 图片上传完成的信号

    def __init__(self, parent = None):
        super(UpLoadImage_Thread,self).__init__(parent)
        self.img_path = ''
        self.result = None
        self.OCR = PaddleOCR(lang='ch')

        # img_path = '..\images\img1.png'
        # result = self.OCR.ocr(img_path)

    def run(self):                                  # start()函数启动线程执行函数
        try:
            self.result = self.OCR.ocr(self.img_path)
            self.imageWidgetCmdSignal.emit(self.result)
            print('image upload button is pressed!')
            self.finishSignal.emit('success')
        except Exception:
            self.finishSignal.emit('Exception')

class DocInput_Thread(QThread):                     # 加载文档的子线程
    finishSignal = pyqtSignal(str)

    def __init__(self,parent = None,getResponse_Thread:getLangChainResponse_Thread = None,fname:str = ''):
        super(DocInput_Thread,self).__init__(parent)
        self.getResponse_Thread = getResponse_Thread
        self.fname = fname

    def run(self):
        try:
            self.getResponse_Thread.llm.loadDocument(self.fname)    # 加载文件，使用rag方法
            self.getResponse_Thread.doc_list.append(self.fname)
            print(self.getResponse_Thread.doc_list)
            # print(f"文档{self.fname}加载完成")
        except Exception as e:
            print(f"读取文档时产生异常:{e}")
        finally:
            self.finishSignal.emit(f"文档{self.fname}加载完成")

class AudioInput_Thread(QThread):                   # 语音输入

    finishSignal = pyqtSignal(str)                  # 语音处理完成的信号

    def __init__(self, parent = None):
        super(AudioInput_Thread,self).__init__(parent)
        self.converter = None
        self.start()

    def run(self):
        if self.converter == None:
            self.converter = MicrophoneListener()
        else:
            self.converter.listen()
            text = self.converter.speech2text()     # 接下来应该产生一个信号把text送出去
